//
//  WmcAmapLocationManager.swift
//  YPWatermarkCamera
//
//  Created by 周诗松 on 2022/4/8.
//

import AMapFoundationKit
import AMapLocationKit
import CoreLocation
import Foundation
import RxRelay
import YPBedrock

// MARK: - [WmcAmapLocationManager]

open class WmcAmapLocationManager: NSObject {
    // Lifecycle

    override init() {
        super.init()
    }

    // Public

    // 定位状态
    public enum Status {
        // 获取中
        case fetching
        // 获取完成
        case completion
        // 获取失败
        case failure
        // 停止
        case stop
    }

    public static var shared: WmcAmapLocationManager = {
        let manager = WmcAmapLocationManager()
        return manager
    }()

    // Internal

    /// 定位状态
    private(set) var location: BehaviorRelay<(status: WmcAmapLocationManager.Status, location: CLLocation?)?> = .init(value: nil)

    /// 隐私合规
    static func requestPrivecy() {
        AMapLocationManager.updatePrivacyAgree(.didAgree)
        AMapLocationManager.updatePrivacyShow(.didShow, privacyInfo: .didContain)
    }

    /// 定位完成后的回调
     var locationHasFinishedBlock: (() -> Void)?
    
    /// 开始自动定位
    static func startUpdateLocation(finishedBlock: (() -> Void)? = nil) {
        shared.locationHasFinishedBlock = finishedBlock
        let status = shared.location.value?.status
        if status != nil, status != .stop {
            return
        }
        WmcAmapLocationManager.clean()
        shared.queue.async {
            shared.locationManager.startUpdatingLocation()
        }
        shared.location.accept((status: .fetching, location: nil))
        WmcLogManager.DebugLog(type: .location, log: "开始定位")
    }

    /// 停止自动定位
    static func stopUpdateLocation() {
        shared.queue.async {
            shared.locationManager.stopUpdatingLocation()
        }
        shared.location.accept((status: .stop, location: nil))
    }

    /// 单次定位
    static func requestLocation(success: @escaping (CLLocation?, AMapLocationReGeocode?) -> Void, failure: ((String) -> Void)? = nil) {
        shared.locationManager.requestLocation(withReGeocode: true) { location, reGeocode, error in
            if let error = error {
                let error = error as NSError

                if error.code == AMapLocationErrorCode.locateFailed.rawValue {
                    // 定位错误：此时location和regeocode没有返回值，不进行annotation的添加
                    NSLog("定位错误:{\(error.code) - \(error.localizedDescription)};")
                    failure?(error.localizedDescription)
                } else if error.code == AMapLocationErrorCode.reGeocodeFailed.rawValue
                    || error.code == AMapLocationErrorCode.timeOut.rawValue
                    || error.code == AMapLocationErrorCode.cannotFindHost.rawValue
                    || error.code == AMapLocationErrorCode.badURL.rawValue
                    || error.code == AMapLocationErrorCode.notConnectedToInternet.rawValue
                    || error.code == AMapLocationErrorCode.cannotConnectToHost.rawValue
                {
                    // 逆地理错误：在带逆地理的单次定位中，逆地理过程可能发生错误，此时location有返回值，regeocode无返回值，进行annotation的添加
                    NSLog("逆地理错误:{\(error.code) - \(error.localizedDescription)};")
                } else {
                    // 没有错误：location有返回值，regeocode是否有返回值取决于是否进行逆地理操作，进行annotation的添加
                }
            } else {
                let model = WmcLocationModel()
                model.location = location

                success(location, reGeocode)
            }
        }
    }

    /// 清除缓存
    static func clean() {
        WmcAmapLocationManager.shared.lastLocation = nil
    }

    // Private

    private lazy var locationManager: AMapLocationManager = {
        let manager = AMapLocationManager()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.locationTimeout = 10
        manager.reGeocodeTimeout = 10
        manager.locatingWithReGeocode = false
        manager.distanceFilter = 20
        return manager
    }()

    /// 上次定位信息
    var lastLocation: CLLocation?

    private var lastReGeocode: AMapLocationReGeocode?

    private let queue: DispatchQueue = .init(label: "com.watermark.amap", attributes: .concurrent)
}

// MARK: - AMapLocationManagerDelegate

extension WmcAmapLocationManager: AMapLocationManagerDelegate {
    public func amapLocationManager(_ manager: AMapLocationManager!, didUpdate location: CLLocation!, reGeocode: AMapLocationReGeocode!) {
        guard let location = location else {
            lastLocation = nil
            self.location.accept((status: .failure, location: nil))
            return
        }

        // 高德sdk在启动时会出现重复回调的情况，进行重复回调的拦截
        if let lastLocation = lastLocation {
            let distance = lastLocation.distance(from: location)
            if distance < 20.0 {
                return
            }
        }
        log("latitude:\(location.coordinate.latitude), longitude: \(location.coordinate.longitude), reGeocode:\(String(describing: reGeocode))")

        self.location.accept((status: .completion, location: location))
        lastLocation = location
        WmcLogManager.DebugLog(type: .location, log: "定位成功")
        YPGCD.Delay(delay: 1) { [weak self] in
            guard let weakSelf = self else { return }
            weakSelf.locationHasFinishedBlock?() // 回调定位完成
            weakSelf.locationHasFinishedBlock = nil // 仅回调单次
        }
    }

    public func amapLocationManager(_ manager: AMapLocationManager!, didFailWithError error: Error!) {
        self.location.accept((status: .failure, location: nil))
        WmcTrack.addEvent(id: "location_failure", params: ["error": error.localizedDescription])
        WmcLogManager.DebugLog(type: .location, log: "定位失败")
    }

    /// 日志系统
    fileprivate func log(_ log: String) {
        WmcLogManager.DebugLog(type: .amapLocationManager, log: log)
    }
}
